package com.hyl.websocket;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.hyl.config.GetHttpSessionConfig;
import com.hyl.utils.MessageUtil;
import com.hyl.websocket.bean.Message;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 针对每一个客户端都会创建一个Endpoint(端点)
 */
//设置访问路径
//引入HttpSession配置类
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    private static final Logger log = LoggerFactory.getLogger(ChatEndpoint.class);

    //保存当前所有在线用户，key为用户名，value为 Session 对象
    //static 共享 final 防止重新赋值 ConcurrentHashMap 线程安全
    private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession; //成员变量，方便当前用户继续使用

    /**
     * 建立websocket连接后，被调用
     * @param wsSession
     */
    @OnOpen
    public void onOpen(Session wsSession, EndpointConfig endpointConfig){  //注意这个是websocket的session
        //用户登录时，已经保存用户名，从HttpSession中获取用户名
        this.httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
        String username = (String) this.httpSession.getAttribute("currentUser");
        if(ObjectUtil.isNotEmpty(username) && !onlineUsers.containsKey(username)){  //避免重复存储
            //存储当前用户以及对应的session进行存储
            onlineUsers.put(username, wsSession);
            //再将当前登录的所有用户以广播的方式进行推送
            String message = MessageUtil.getMessage(true, null, getAllFriendUsernames(), false);
            broadcastAllUsers(message);
        }
    }

    public Set getAllFriendUsernames(){
        Set<String> set = onlineUsers.keySet();
        return set;
    }

    /**
     * 广播方法
     * 系统消息格式：{"system":true,"fromName":null,"message":"xxx"}
     */
    private void broadcastAllUsers(String message){
        if(ObjectUtil.isEmpty(message)){
            return;
        }
        try{
            //遍历当前在线用户的map集合
            Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
            for (Map.Entry<String, Session> entry : entries) {
                //获取所有用户的session对象
                Session session = entry.getValue();
                if(session.isOpen()){
                    //获取同步消息发送的实例，发送文本消息
                    session.getBasicRemote().sendText(message);
                }else {
                    onlineUsers.remove(entry.getKey());
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            log.info("广播已发送，消息：{}", message);
        }
    }

    /**
     * 接收到浏览器(客户端)发送的数据时被调用，如：张三 -> 李四
     * @param message 发送的数据是JSON字符串 ，格式为：{"toName":"xxx","message":"xxx"}
     */
    @OnMessage
    public void onMessage(String message){
        //当前登录的用户
        String username = null;
        //当前用户发送的消息
        String messageText = null;
        //接收人用户名
        String toUsername = null;
        try {
            //将消息推送给指定用户
            //操作JSON字符串消息需先转换为对应的Message对象
            Message msg = JSON.parseObject(message, Message.class);
            //获取接收方用户名称
            toUsername = msg.getToName();
            //获取接收方需收到的消息
            messageText = msg.getMessage();
            //获取消息接收方用户对象的session对象发消息
            Session toUserSession = onlineUsers.get(toUsername);
            //封装发送的消息
            username = (String) this.httpSession.getAttribute("currentUser");
            String toUserMsg = MessageUtil.getMessage(false, username, messageText, false);
            toUserSession.getBasicRemote().sendText(toUserMsg);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            log.info("发送人：{}，接收人：{}，消息内容：{}", username, toUsername, messageText);
        }
    }

    /**
     * 断开websocket连接被调用
     * @param wsSession
     */
    @OnClose
    public void onClose(Session wsSession){  //注意这个是websocket的session
        //从在线用户map中剔除当前退出登录的用户
        String username = (String) this.httpSession.getAttribute("currentUser");
        if(ObjectUtil.isNotNull(username)){
            onlineUsers.remove(username);
            //通知其他用户，当前用户下线
            String leaveMessage = MessageUtil.getMessage(true, null, username + "，已下线",true);
            broadcastAllUsers(leaveMessage);
            //就是重新发送广播，当前在线用户的map的key
            String message = MessageUtil.getMessage(true, null, getAllFriendUsernames(),false);
            broadcastAllUsers(message);
        }
    }
}
